
#include <BStyle>
#include <BFont>
#include <BImage>
#include <BReadWrite>
#include <BApplication>
#include <BSystem>

#include <BFileDialog>
#include "member_BFileDialog.h"
#include "IGlobal.h"

using namespace BWE;

#define member					(*(member_BFileDialog*)_ptr)
#define member_allocate()		_ptr = new member_BFileDialog(this)
#define member_release()		delete (member_BFileDialog*)_ptr

BFileDialog* GetFileDialog()
{
	static BHolder<BFileDialog> fileDialog = new BFileDialog();
	fileDialog->setModal(true);
	return fileDialog;
}

const BString& BFileDialog::OpenEntry(BWidget* parent, const BString& title, const BString& dir)
{
	BFileDialog* fileDialog = GetFileDialog();
	fileDialog->setTitleIcon(BSystem::EntryIcon(dir));
	fileDialog->setParent(parent);
	fileDialog->setTitle(title);
	if (dir.size())
		fileDialog->setPath(dir);
	fileDialog->setAttribFilter(Attrib_All);
	fileDialog->setSelectMultiple(false);
	fileDialog->execute();
	return fileDialog->entryName();
}
const BString& BFileDialog::OpenEntry(const BString& title, const BString& dir)
{
	BFileDialog* fd = GetFileDialog();
	fd->setParent(App_MainWindow());
	fd->setTitle(title);
	if (dir.size())
		fd->setPath(dir);
	fd->setAttribFilter(Attrib_All);
	fd->setSelectMultiple(false);
	fd->execute();
	return fd->entryName();
}
const BStringList& BFileDialog::OpenEntries(BWidget* parent, const BString& title, const BString& dir)
{
	BFileDialog* fileDialog = GetFileDialog();
	fileDialog->setTitleIcon(BSystem::EntryIcon(dir));
	fileDialog->setParent(parent);
	fileDialog->setTitle(title);
	if (dir.size())
		fileDialog->setPath(dir);
	fileDialog->setAttribFilter(Attrib_All);
	fileDialog->setSelectMultiple(true);
	fileDialog->execute();
	return fileDialog->entryNameList();
}
const BStringList& BFileDialog::OpenEntries(const BString& title, const BString& dir)
{
	BFileDialog* fd = GetFileDialog();
	fd->setParent(App_MainWindow());
	fd->setTitle(title);
	if (dir.size())
		fd->setPath(dir);
	fd->setAttribFilter(Attrib_All);
	fd->setSelectMultiple(false);
	fd->execute();
	return fd->entryNameList();
}

const BString& BFileDialog::OpenFolder(BWidget* parent, const BString& title, const BString& dir)
{
	BFileDialog* fileDialog = GetFileDialog();
	fileDialog->setTitleIcon(BSystem::EntryIcon(dir));
	fileDialog->setParent(parent);
	fileDialog->setTitle(title);
	if (dir.size())
		fileDialog->setPath(dir);
	fileDialog->setAttribFilter(Attrib_Folder);
	fileDialog->setSelectMultiple(false);
	fileDialog->execute();
	return fileDialog->entryName();
}
const BString& BFileDialog::OpenFolder(const BString& title, const BString& dir)
{
	BFileDialog* fd = GetFileDialog();
	fd->setParent(App_MainWindow());
	fd->setTitle(title);
	if (dir.size())
		fd->setPath(dir);
	fd->setAttribFilter(Attrib_Folder);
	fd->setSelectMultiple(false);
	fd->execute();
	return fd->entryName();
}
const BStringList& BFileDialog::OpenFolders(BWidget* parent, const BString& title, const BString& dir)
{
	BFileDialog* fileDialog = GetFileDialog();
	fileDialog->setTitleIcon(BSystem::EntryIcon(dir));
	fileDialog->setParent(parent);
	fileDialog->setTitle(title);
	if (dir.size())
		fileDialog->setPath(dir);
	fileDialog->setAttribFilter(Attrib_Folder);
	fileDialog->setSelectMultiple(true);
	fileDialog->execute();
	return fileDialog->entryNameList();
}
const BStringList& BFileDialog::OpenFolders(const BString& title, const BString& dir)
{
	BFileDialog* fd = GetFileDialog();
	fd->setParent(App_MainWindow());
	fd->setTitle(title);
	if (dir.size())
		fd->setPath(dir);
	fd->setAttribFilter(Attrib_Folder);
	fd->setSelectMultiple(false);
	fd->execute();
	return fd->entryNameList();
}

const BString& BFileDialog::OpenFile(BWidget* parent, const BString& title, const BString& dir, const BString& filter)
{
	BFileDialog* fileDialog = GetFileDialog();
	fileDialog->setTitleIcon(BSystem::EntryIcon(dir));
	fileDialog->setParent(parent);
	fileDialog->setTitle(title);
	if (dir.size())
		fileDialog->setPath(dir);
	if (filter.size())
		fileDialog->setNameFilter(filter);
	fileDialog->setAttribFilter(Attrib_File);
	fileDialog->setSelectMultiple(false);
	fileDialog->execute();
	return fileDialog->entryName();
}
const BString& BFileDialog::OpenFile(const BString& title, const BString& dir, const BString& filter)
{
	BFileDialog* fd = GetFileDialog();
	fd->setParent(App_MainWindow());
	fd->setTitle(title);
	if (dir.size())
		fd->setPath(dir);
	if (filter.size())
		fd->setNameFilter(filter);
	fd->setAttribFilter(Attrib_File);
	fd->setSelectMultiple(false);
	fd->execute();
	return fd->entryName();
}
const BStringList& BFileDialog::OpenFiles(BWidget* parent, const BString& title, const BString& dir, const BString& filter)
{
	BFileDialog* fileDialog = GetFileDialog();
	fileDialog->setTitleIcon(BSystem::EntryIcon(dir));
	fileDialog->setParent(parent);
	fileDialog->setTitle(title);
	if (dir.size())
		fileDialog->setPath(dir);
	if (filter.size())
		fileDialog->setNameFilter(filter);
	fileDialog->setAttribFilter(Attrib_All);
	fileDialog->setSelectMultiple(true);
	fileDialog->execute();
	return fileDialog->entryNameList();
}
const BStringList& BFileDialog::OpenFiles(const BString& title, const BString& dir, const BString& filter)
{
	BFileDialog* fd = GetFileDialog();
	fd->setParent(App_MainWindow());
	fd->setTitle(title);
	if (dir.size())
		fd->setPath(dir);
	if (filter.size())
		fd->setNameFilter(filter);
	fd->setAttribFilter(Attrib_All);
	fd->setSelectMultiple(true);
	fd->execute();
	return fd->entryNameList();
}

const BString& BFileDialog::SaveFile(BWidget* parent, const BString& title, const BString& dir, const BString& filter)
{
	BFileDialog* fileDialog = GetFileDialog();
	fileDialog->setTitleIcon(BSystem::EntryIcon(dir));
	fileDialog->setParent(parent);
	fileDialog->setTitle(title);
	if (dir.size())
		fileDialog->setPath(dir);
	if (filter.size())
		fileDialog->setNameFilter(filter);
	fileDialog->setAttribFilter(Attrib_All);
	fileDialog->setSelectMultiple(false);
	fileDialog->execute();
	return fileDialog->entryName();
}
const BString& BFileDialog::SaveFile(const BString& title, const BString& dir, const BString& filter)
{
	BFileDialog* fd = GetFileDialog();
	fd->setParent(App_MainWindow());
	fd->setTitle(title);
	if (dir.size())
		fd->setPath(dir);
	if (filter.size())
		fd->setNameFilter(filter);
	fd->setAttribFilter(Attrib_All);
	fd->setSelectMultiple(true);
	fd->execute();
	return fd->entryName();
}

BFileDialog::BFileDialog(BWidget* parent) : BDialog(parent)
{
	member_allocate();
	this->setSize(800, 560);
}
BFileDialog::~BFileDialog()
{
	member_release();
}

void BFileDialog::setEditable(bool editable)
{
	if (editable && member.itemEdit.empty())
	{
		member.itemEdit = new BLineEdit();
		member.itemEdit->hide();
		member.connect(member.itemEdit, Signal_Focused, &member_BFileDialog::slotItemEditFocused);
		member.connect(member.itemEdit, Signal_EditFinished, &member_BFileDialog::slotItemEditFinished);
		return;
	}
	if (!editable && member.itemEdit.valid())
	{
		member.itemEdit->setAsset(BValue());
		member.itemEdit->setParent(0);
		member.itemEdit = 0;
		return;
	}
}
bool BFileDialog::editable() const
{
	return member.itemEdit;
}

void BFileDialog::setAttribFilter(Attrib attribFilter)
{
	if (member.attribFilter != attribFilter)
	{
		member.attribFilter = attribFilter;
		member.selectEntries();
	}
}
Attrib BFileDialog::attribFilter() const
{
	return member.attribFilter;
}

void BFileDialog::setNameFilter(const BString& nameFilter)
{
	if (member.nameFilter != nameFilter)
	{
		member.nameFilter = nameFilter;
		member.filterComboBox->clearItems();
		BString lineText;
		int pos = 0;
		while (pos < member.nameFilter.size())
		{
			pos = member.nameFilter.line(lineText, pos);
			lineText.trimmed();
			member.filterComboBox->addItem(lineText);
		}
		member.filterComboBox->setCurrentIndex(0);
	}
}
const BString& BFileDialog::nameFilter() const
{
	return member.nameFilter;
}

void BFileDialog::setSelectMultiple(bool selectMultiple)
{
	if (member.selectMultiple != selectMultiple)
	{
		member.selectMultiple = selectMultiple;
		member.tableWidget->setSelectMode(member.tableWidget->selectMode() | Select_Multiple);
	}
}
bool BFileDialog::selectMultiple() const
{
	return member.selectMultiple;
}

void BFileDialog::setImageExts(const BString& imageExts)
{
	if (member.imageExts != imageExts)
	{
		member.imageExts = imageExts;
		member.freshTableItems();
	}
}
const BString& BFileDialog::imageExts() const
{
	return member.imageExts;
}

void BFileDialog::setPath(const BString& path)
{
	member.openFolder(path);
}
const BString& BFileDialog::path() const
{
	return member.pathComboBox->text();
}

const BString& BFileDialog::entryName() const
{
	return member.entryNames.first();
}
const BStringList& BFileDialog::entryNameList() const
{
	return member.entryNames;
}

bool BFileDialog::input(const BString& text, const BValue& value)
{
	if (text == "path-combobox")
	{
		if (BComboBox* pathComboBox = value)
		{
			int index = member.topLayout->index(member.pathComboBox);
			member.topLayout->remove(index);
			member.topLayout->insertWidget(index, pathComboBox, 100);
			member.pathComboBox = pathComboBox;
			member.connect(pathComboBox, Signal_TextChanged, &member_BFileDialog::slotPathTextChanged);
			return true;
		}
		return false;
	}
	if (text == "editable")
	{
		bool editable = value;
		this->setEditable(editable);
		return true;
	}
	if (text == "attrib-filter")
	{
		Attrib attribFilter = value;
		this->setAttribFilter(attribFilter);
		return true;
	}
	if (text == "name-filter")
	{
		const BString& nameFilter = value;
		this->setNameFilter(nameFilter);
		return true;
	}
	if (text == "image-exts")
	{
		const BString& imageExts = value;
		this->setImageExts(imageExts);
		return true;
	}
	if (text == "path")
	{
		const BString& path = value;
		this->setPath(path);
		return true;
	}
	return BDialog::input(text, value);
}
bool BFileDialog::query(const BString& text, BValue& value) const
{
	if (text == "path-combobox")
	{
		value = member.pathComboBox;
		return false;
	}
	if (text == "editable")
	{
		value = member.itemEdit.valid();
		return true;
	}
	if (text == "attrib-filter")
	{
		value = member.attribFilter;
		return true;
	}
	if (text == "name-filter")
	{
		BString& text = value.fill<BString>();
		text = member.nameFilter;
		return true;
	}
	if (text == "file-exts")
	{
		BStringArray& fileExts = value.fill<BStringArray>();
		fileExts = member.fileExts;
		return true;
	}
	if (text == "image-exts")
	{
		BString& text = value.fill<BString>();
		text = member.imageExts;
		return true;
	}
	if (text == "path")
	{
		BString& text = value.fill<BString>();
		text = member.pathComboBox->text();
		return true;
	}
	return BDialog::query(text, value);
}

void BFileDialog::showEvent(const BEvent& event)
{
	BDialog::showEvent(event);
	if (member.pathComboBox->text().empty())
	{
		BString current = BApplication::CurrentPath();
		member.openFolder(current);
	}
}
void BFileDialog::closeEvent(const BEvent& event)
{
	BDialog::closeEvent(event);
	member.entryNames.clear();
	if (this->result() > 0)
	{
		const BString& path = member.pathComboBox->text();
		const BString& text = member.nameEdit->text();
		int begin = 0;
		int end = text.find(',');
		while (begin < end)
		{
			BString& name = member.entryNames.append();
			text.substr(name, begin, end);
			if (!path.endWith('/'))
				name.prepend('/');
			name.prepend(path);
			begin = end + 1;
			end = text.find(',', begin);
		}
	}
}

void BFileDialog::styleEvent(const BEvent& event)
{
	BDialog::styleEvent(event);
	member.style = this->realStyle();
	member.dirtyStyle = true;
	if (member.style)
	{
		const BFont* font = member.style->font();
		if (font)
		{
			member.splitLayout->setSpacing(font->size() / 2);

			int iconSize = font->size() / 2;
			member.prevButton->setIconSize(iconSize);
			member.nextButton->setIconSize(iconSize);
			member.aboveButton->setIconSize(iconSize);

		}
		for (int i = 0; i < member.viewComboBox->itemCount(); i++)
		{
			BComboItem* item = member.viewComboBox->item(i);
			if (BIcon* icon = member.style->icon(item->name()))
			{
				item->setIcon(icon);
			}
		}
	}
}
void BFileDialog::freshEvent(const BEvent& event)
{
	BDialog::freshEvent(event);
	if (member.dirtyStyle)
	{
		for (int i = 0; i < member.treeWidget->topItemCount(); i++)
		{
			member.freshTreeItem(member.treeWidget->topItem(i));
		}
		member.freshListItems();
		member.freshTableItems();
		member.dirtyStyle = false;
	}
}
